package com.sugon.controller.device;

import cn.afterturn.easypoi.entity.vo.TemplateExcelConstants;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.view.PoiBaseView;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.sugon.annotation.AutoLog;
import com.sugon.annotation.DataFilter;
import com.sugon.annotation.LimitSubmit;
import com.sugon.annotation.NonBusinessParamValid;
import com.sugon.entity.*;
import com.sugon.modules.base.consts.BusModule;
import com.sugon.modules.device.consts.DevMaintenanceTicketStatus;
import com.sugon.modules.device.consts.DevMaintenanceTicketType;
import com.sugon.modules.device.consts.ExceptionSourceType;
import com.sugon.modules.device.model.vo.*;
import com.sugon.modules.device.service.IDeviceMaintenanceTicketManager;
import com.sugon.modules.sys.service.INoticeManager;
import com.sugon.service.*;
import com.sugon.utils.PageUtils;
import com.sugon.utils.Query;
import com.sugon.utils.R;
import com.sugon.utils.ShiroUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 设备维护工单列表
 *
 * @author YuXD
 * @date 2020-11-18 16:07
 */
@Controller
@RequestMapping("devplangc")
public class DevMaintenanceTicketController {

    @Resource
    private DevPlanGcService devPlanGcService;
    @Resource
    private ExcepRecordService excepRecordService;
    @Resource
    private DevMaintenanceTicketService devPmTicketService;
    @Resource
    private DeviceInfoService deviceInfoService;
    @Resource
    private IDeviceMaintenanceTicketManager deviceMaintenanceTicketManager;
    @Resource
    private DevMaintenancePlanChangeService devMaintenancePlanChangeService;
    @Resource
    private TicketLiveImageService ticketLiveImageService;
    @Resource
    private INoticeManager noticeManager;
    @Resource
    private SysConfigService configService;

    @AutoLog(value = "导出PM工单执行状况统计数据", busModule = BusModule.DEVICE_MAINTENANCE)
    @RequiresPermissions("devplangc:export")
    @RequestMapping("/export")
    @ResponseBody
    public void export(@RequestParam Map<String, Object> params, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response) {
        // 设置导出EXCEL模板名称
        modelMap.put(TemplateExcelConstants.FILE_NAME, "PM工单执行统计_" + DateUtil.format(DateUtil.date(), "yyyyMMdd"));
        // 设置导出模板参数
        TemplateExportParams templateExportParams = new TemplateExportParams(configService.getPoiTemplatePath() + "amTicketStatTemplate.xlsx");
        modelMap.put(TemplateExcelConstants.PARAMS, templateExportParams);
        // 获取设备维护工单执行状况统计数据
        modelMap.put(TemplateExcelConstants.MAP_DATA, deviceMaintenanceTicketManager.prepareExportData(params));
        PoiBaseView.render(modelMap, request, response, TemplateExcelConstants.EASYPOI_TEMPLATE_EXCEL_VIEW);
    }

    /**
     * 查看列表
     */
    @RequestMapping("/list")
//    @RequiresPermissions("devplangc:list")
    @DataFilter(deptAlias = "DMT.WORKSHOP", userAlias = "DMT.EXECUTOR")
    @ResponseBody
    public R list(@RequestParam Map<String, Object> params) {
        // 如果查询的执行时间不为空，那么回填年度和周次
        Date executeTime = MapUtil.getDate(params, "executeTime");
        if (executeTime != null) {
            params.put("yearly", DateUtil.year(executeTime));
            params.put("weekOfYear", DateUtil.weekOfYear(executeTime));
            params.remove("executeTime");
        }
        /*
            查看工单结束日期所在的周次查询
         */
        Date executeEndTime = MapUtil.getDate(params, "executeEndTime");
        if (executeEndTime != null) {
            params.put("startTime", DateUtil.formatDateTime(DateUtil.beginOfWeek(executeEndTime)));
            params.put("endTime", DateUtil.formatDateTime(DateUtil.endOfWeek(executeEndTime)));
            params.remove("executeEndTime");
        }
        // 获取工单状态
        String status = MapUtil.getStr(params, "status");
        if (StrUtil.isNotEmpty(status)) {
            params.put("statusList", StrUtil.split(status, ","));
            params.remove("status");
        }
        /*
            PM完成率的维护工单列表也会调用这个接口
            seriesName 和seriesIndex
               seriesName有两种情形：2023-01 和 第1周两种情形
               seriesIndex：0 1 2 3四个值，其中0和2查看已完成的，1和3查看其它情况的
         */
        //  seriesName有两种情形：2023-01 和 第1周两种情形,简单处理
        String seriesName = MapUtil.getStr(params, "seriesName");
        if (StrUtil.isNotEmpty(seriesName)) {
            if (StrUtil.containsAny(seriesName, "第", "周")) {
                // 获取本月的第几周
                Integer weekOfMonth = ReUtil.getFirstNumber(seriesName);
                // 获取本月第weekOfMonth周的第一天
                DateTime dateTime = DateUtil.offsetWeek(DateUtil.beginOfMonth(DateUtil.date()), weekOfMonth - 1);
                params.put("startTime", DateUtil.formatDateTime(DateUtil.beginOfWeek(dateTime)));
                params.put("endTime", DateUtil.formatDateTime(DateUtil.endOfWeek(dateTime)));
            } else if (StrUtil.containsAny(seriesName, "-")) {
                DateTime currentMonth = DateUtil.parseDate(seriesName + "-01");
                params.put("startTime", DateUtil.formatDateTime(DateUtil.beginOfMonth(currentMonth)));
                params.put("endTime", DateUtil.formatDateTime(DateUtil.endOfMonth(currentMonth)));
            }
        }
        // seriesIndex：0 1 2 3四个值，其中0和2查看已完成的，1和3查看其它情况的
        Integer seriesIndex = MapUtil.getInt(params, "seriesIndex");
        if (seriesIndex != null) {
            //
            if (seriesIndex == 0 || seriesIndex == 2) {
                params.put("statusList", CollectionUtil.newArrayList(DevMaintenanceTicketStatus.COMPLETE, DevMaintenanceTicketStatus.DELAY_COMPLETE));
            } else if (seriesIndex == 1 || seriesIndex == 3) {
                params.put("statusList", CollectionUtil.newArrayList(DevMaintenanceTicketStatus.DISPATCHING, DevMaintenanceTicketStatus.WAIT_EXECUTE, DevMaintenanceTicketStatus.MAINTAINING, DevMaintenanceTicketStatus.DELAY_APPLYING, DevMaintenanceTicketStatus.DELAY_MAINTAINING, DevMaintenanceTicketStatus.STOP, DevMaintenanceTicketStatus.OVERTIME, DevMaintenanceTicketStatus.CANCELLING, DevMaintenanceTicketStatus.CANCEL));
            }
        }
        //查询列表数据
        Query query = new Query(params);
        List<DevMaintenanceTicketVO> devPlanGcList = devPmTicketService.queryAll(query);
        int total = devPmTicketService.queryAllTotal(query);
        PageUtils pageUtil = new PageUtils(devPlanGcList, total, query.getLimit(), query.getPage());
        return R.ok().put("page", pageUtil);
    }

    /**
     * 获取设备维护工单现场图片
     */
    @RequestMapping("/{ticketId}/liveImages")
    @ResponseBody
    public R loadLiveImages(@PathVariable("ticketId") String ticketId) {
        return R.ok().put("liveImageList", ticketLiveImageService.getTicketLiveImages(null, ticketId, "MAINTENANCE", "PM"));
    }

    /**
     * 查看信息
     */
    @RequestMapping("/info/{id}")
//    @RequiresPermissions("devplangc:info")
    @ResponseBody
    public R info(@PathVariable("id") String id) {
        DevPlanGcEntity devPlanGc = devPlanGcService.queryObject(id);

        return R.ok().put("devPlanGc", devPlanGc);
    }

    @AutoLog(value = "新增PM检修计划", busModule = BusModule.DEVICE_MAINTENANCE)
    @LimitSubmit(key = "addTmpPmTicket:%s")
    @NonBusinessParamValid
    @RequestMapping("/save")
    @RequiresPermissions("devplangc:save")
    @ResponseBody
    public R save(@Validated @RequestBody DevTemMaintenanceTicketParamVO devPlanGc, BindingResult bindingResult) {
        // 校验设备信息是否存在
        DeviceInfoEntity device = deviceInfoService.queryObject(devPlanGc.getDevId());
        if (device == null) {
            return R.error("设备信息不存在");
        }
        devPlanGc.setTicketType(DevMaintenanceTicketType.TEM);
        devPlanGc.setWorkshopId(device.getDevFarm());
        devPmTicketService.addTemporaryPlan(devPlanGc);
        return R.ok();
    }

    /**
     * 修改
     */
    @AutoLog("设备预防维护工单-修改")
    @RequestMapping("/update")
    @RequiresPermissions("devplangc:update")
    @ResponseBody
    public R update(@RequestBody DevPlanGcEntity devPlanGc) {
        devPlanGcService.update(devPlanGc);

        return R.ok();
    }

    /**
     * 权限同save一致
     *
     * @param ids
     * @return
     */
    @AutoLog("设备预防维护工单-删除")
    @RequestMapping("/delete")
    @RequiresPermissions("devplangc:save")
    @ResponseBody
    public R delete(@RequestBody String[] ids) {
        // 校验：只可以删除自己车间的未执行的检修工单
        for (String id : ids) {
            DevMaintenanceTicketEntity devPmTicket = devPmTicketService.queryObject(id);
            if (devPmTicket == null) {
                continue;
            }
            if (!(StrUtil.equals(ShiroUtils.getWorkshop(), devPmTicket.getWorkshop()) && StrUtil.equals(DevMaintenanceTicketType.TEM.name(), devPmTicket.getTicketType()) && devPmTicket.getStatus() < DevMaintenanceTicketStatus.MAINTAINING)) {
                return R.error("只可以删除自己车间的未执行的检修工单！");
            }
        }
        devPmTicketService.deleteBatch(ids);
        return R.ok();
    }

    /**
     * 查看所有列表
     */
    @RequestMapping("/queryAll")
    @ResponseBody
    public R queryAll(@RequestParam Map<String, Object> params) {

        List<DevPlanGcEntity> list = devPlanGcService.queryList(params);

        return R.ok().put("list", list);
    }

    @LimitSubmit(key = "dispatchAmTicket:%s")
    @NonBusinessParamValid
    @AutoLog(value = "PM工单派工", busModule = BusModule.DEVICE_MAINTENANCE)
    @RequestMapping("/dispatchTicket")
    @RequiresPermissions("devplangc:addExecuteUser")
    @ResponseBody
    public R addExecuteUser(@Validated @RequestBody DevMaintenanceTicketDispatchParamVO ticketDispatchParam, BindingResult bindingResult) {
        // 由于数据库字段长度限制，目前最多派工7个人
        List<String> executor = ticketDispatchParam.getExecutor();
        if (executor.size() > 7) {
            return R.error("目前最多支持派工7个人！");
        }

        List<String> ticketIdList = ticketDispatchParam.getTicketIdList();
        DevMaintenanceTicketEntity maintenanceTicket = null;

        // 获取不能再次派工的工单状态列表，后续用做比较用
        List<Integer> unDispatchableTicketStatusList = CollectionUtil.newArrayList(DevMaintenanceTicketStatus.DISPATCHING, DevMaintenanceTicketStatus.WAIT_EXECUTE, DevMaintenanceTicketStatus.OVERTIME);

        DateTime current = DateUtil.date();

        for (String ticketId : ticketIdList) {
            // 1、校验工单是否存在以及是否完成
            maintenanceTicket = devPmTicketService.queryObject(ticketId);
            if (maintenanceTicket == null) {
                return R.error("设备维护工单不存在，请确认！");
            }
            // 校验是否可派工
            if (!CollectionUtil.contains(unDispatchableTicketStatusList, maintenanceTicket.getStatus())) {
                return R.error("只有 [待分派、待执行、已超期] 状态工单才可以派工！");
            }
            // 只有工单开始日期和当前系统日期是同一个周时才可以派工
            if (DateUtil.weekOfYear(maintenanceTicket.getStartTime()) > DateUtil.weekOfYear(current)) {
                return R.error("不得过早分派工单，只能分派本周内待执行的工单！");
            }
            // 重新设置维护人员
            maintenanceTicket.setExecutor(CollectionUtil.join(executor, ","));
            /*
                目前可以派工的有两种：待分派、已超期
                只有待分派的工单需要调整为已派工，已超期的不进行
             */
            if (DevMaintenanceTicketStatus.DISPATCHING.equals(maintenanceTicket.getStatus())) {
                maintenanceTicket.setStatus(DevMaintenanceTicketStatus.WAIT_EXECUTE);
            }
            maintenanceTicket.setDispatchTime(current);
            int updateFlag = devPmTicketService.update(maintenanceTicket);
            if (updateFlag > 0) {
                // 给维护人员发送消息
                noticeManager.pushDeviceMaintenanceNotice(maintenanceTicket.getId());
            }
        }
        return R.ok();
    }

    @NonBusinessParamValid
    @AutoLog(value = "PM工单变更申请", busModule = BusModule.DEVICE_MAINTENANCE)
    @RequestMapping("/applyDelay")
    @RequiresPermissions("devplangc:delay")
    @ResponseBody
    public R applyChange(@Validated @RequestBody DevMaintenanceDelayApplyParam ticketChangeApplyParam, BindingResult bindingResult) {
        // 1、校验工单是否存在以及是否完成
        DevMaintenanceTicketEntity ticket = devPmTicketService.queryObject(ticketChangeApplyParam.getTicketId());
        if (ticket == null) {
            return R.error("设备维护工单不存在，请确认！");
        }
        // 延期申请中的不允许申请
        if (DevMaintenanceTicketStatus.DELAY_APPLYING.equals(ticket.getStatus())) {
            return R.error("工单延期申请中，请耐心等待！");
        }
        // 已完成、已关闭、已延期完成的工单不允许延期
        List<Integer> temStatusList = CollectionUtil.newArrayList(DevMaintenanceTicketStatus.COMPLETE, DevMaintenanceTicketStatus.STOP, DevMaintenanceTicketStatus.DELAY_COMPLETE);
        if (temStatusList.contains(ticket.getStatus())) {
            return R.error("工单已完成或已关闭，不允许申请延期！");
        }
        // 只可以延期申请自己负责的工单
        String applier = ShiroUtils.getUserId();
        if (ticket.getExecutor() == null || !StrUtil.contains(ticket.getExecutor(), applier)) {
            return R.error("只可以申请延期自己负责的设备维护工单！");
        }
        // 期望延期时间不得早于工单最晚结束时间
        if (ticketChangeApplyParam.getChangeTime().before(ticket.getFinalEndTime())) {
            return R.error("期望延期时间不得早于工单结束时间！");
        }
        ticketChangeApplyParam.setApplier(applier);
        ticketChangeApplyParam.setDevMaintenanceTicket(ticket);
        return deviceMaintenanceTicketManager.applyChange(ticketChangeApplyParam) ? R.ok() : R.error();
    }

    @NonBusinessParamValid
    @AutoLog(value = "PM工单延期审核", busModule = BusModule.DEVICE_MAINTENANCE)
    @RequestMapping("/delayApply")
    @RequiresPermissions("devplangc:delayApply")
    @ResponseBody
    public R delayApply(@Validated @RequestBody DelayCheckParam delayCheckParam, BindingResult bindingResult) {
        // 1、校验工单是否存在以及是否完成
        DevMaintenanceTicketEntity ticket = devPmTicketService.queryObject(delayCheckParam.getTicketId());
        if (ticket == null) {
            return R.error("设备维护工单不存在，请确认！");
        }
        // 只有延期申请中的设备维护工单才可以审核
        if (!DevMaintenanceTicketStatus.DELAY_APPLYING.equals(ticket.getStatus())) {
            return R.error("只可以审核处于[延期申请中]的设备维护工单！");
        }
        // 校验该维护工单的延期申请记录是否存在
        DevMaintenancePlanChangeEntity applyRecord = devMaintenancePlanChangeService.queryUnHandledByTicketId(delayCheckParam.getTicketId());
        if (applyRecord == null) {
            return R.error("该工单的延期申请记录不存在，请确认！");
        }
        delayCheckParam.setApplyRecord(applyRecord);
        delayCheckParam.setTicket(ticket);
        return deviceMaintenanceTicketManager.checkChange(delayCheckParam) ? R.ok() : R.error();
    }

    /**
     * @return
     */
    @AutoLog(value = "PM工单异常添加", busModule = BusModule.DEVICE_MAINTENANCE)
    @RequestMapping("/error")
    @RequiresPermissions("devplangc:error")
    @ResponseBody
    public R error(@RequestBody ExcepRecordEntity excepRecordEntity) {
        String devId = excepRecordEntity.getDevId();
        if (StrUtil.isEmpty(devId)) {
            return R.error("发生异常设备必填！");
        }
        DeviceInfoEntity deviceInfo = deviceInfoService.queryObject(devId);
        if (deviceInfo != null) {
            excepRecordEntity.setWorkshopId(deviceInfo.getDevFarm());
        }
        excepRecordEntity.setDutyUserId(ShiroUtils.getUserId());
        excepRecordEntity.setCreateUser(ShiroUtils.getUserId());
        excepRecordEntity.setExType(ExceptionSourceType.PM);
        return excepRecordService.save(excepRecordEntity) > 0 ? R.ok() : R.error();
    }

    /**
     * 催办保存
     *
     * @param
     * @return
     */
    @RequestMapping("/urgeSave")
    @ResponseBody
    @RequiresPermissions("devplangc:urge")
    public R urgeSave(@RequestBody DevReportUrgeEntity devReportUrge) {
        return deviceMaintenanceTicketManager.urge(devReportUrge.getRepairNo(), ShiroUtils.getUserId()) ? R.ok() : R.error();
    }

}
